home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / Music / PLAY / MultiPlayer / NotePlayer / notesys.asm < prev   
Assembly Source File  |  1995-08-23  |  21KB  |  703 lines

  1. * notesys.asm - NotePlayer module for Amiga using system (audio.device)
  2. * Copyright 1992 Bryan Ford
  3. * See distribution terms in NotePlayer.doc
  4. * $Id: notesys.asm,v 3.1 92/09/14 18:35:19 BAF Exp Locker: BAF $
  5.  
  6.         include "exec/types.i"
  7.         include "exec/io.i"
  8.         include "exec/tasks.i"
  9.         include "exec/funcdef.i"
  10.         include "exec/exec_lib.i"
  11.         include "devices/audio.i"
  12.         include "bry/macros.i"
  13.  
  14. VERSION         equ     1       ; Version of Player.doc this NotePlayer is written for
  15. REVISION        equ     0
  16.  
  17. IOREQS          equ     3*4+2   ; Two WRITEs and another command per channel, plus two extra for good measure
  18.  
  19. STACKSIZE       equ     256
  20.  
  21. CLOCK           equ     3579545
  22.  
  23.         code    text
  24.  
  25. *** NoteSys - Jump table providing standard note player interface
  26.         xdef    NoteSys,_NoteSys
  27. NoteSys
  28. _NoteSys
  29.         jmp     noteinfo(pc)
  30.         jmp     noteinit(pc)
  31.         jmp     notefinish(pc)
  32.         jmp     notestart(pc)
  33.         jmp     notestop(pc)
  34.         jmp     notefreqvol(pc)
  35.         jmp     notefreq(pc)
  36.         jmp     notevol(pc)
  37.  
  38. *** NoteSysMasterVol - Set the (mono) master volume at which to play notes
  39. * d0 = New master volume, 0-$100
  40.         xdef    NoteSysMasterVol,_NoteSysMasterVol,@NoteSysMasterVol
  41. _NoteSysMasterVol
  42.         move.w  4+2(sp),d0
  43. NoteSysMasterVol
  44. @NoteSysMasterVol
  45.         move.w  d0,d1
  46.         bra     NoteSysMasterVolBal
  47.  
  48. *** NoteSysMasterVolBal - Set the master volume/balance
  49. * d0 = New master volume for left channels, 0-$100
  50. * d1 = New master volume for right channels, 0-$100
  51.         xdef    NoteSysMasterVolBal,_NoteSysMasterVolBal,@NoteSysMasterVolBal
  52. _NoteSysMasterVolBal
  53.         movem.l 4(sp),d0/d1
  54. NoteSysMasterVolBal
  55. @NoteSysMasterVolBal
  56.         movem.l d2/d6-d7/a4/a6,-(sp)
  57.         lea     k,a4
  58.  
  59.         st.b    volinitflag-k(a4)
  60.  
  61.         lsr.w   #2,d0
  62.         lsr.w   #2,d1
  63.  
  64.         cmp.b   mastervoltab+1-1-k(a4),d0
  65.         bne     \changed
  66.         cmp.b   mastervoltab+2-1-k(a4),d1
  67.         beq     \out
  68. \changed
  69.         move.b  d0,mastervoltab-1+1-k(a4)
  70.         move.b  d1,mastervoltab-1+2-k(a4)
  71.         move.b  d1,mastervoltab-1+4-k(a4)
  72.         move.b  d0,mastervoltab-1+8-k(a4)
  73.  
  74.         move.b  channels-k(a4),d2
  75.         subq.b  #1,d2
  76. \loop
  77.         bsr     findquickreq
  78.         bz      \next
  79.         move.w  chanpervol-k(a4,d6.w),ioa_Period(a1)
  80.         move.w  IO_UNIT+2(a1),d1
  81.         moveq   #0,d0
  82.         move.b  mastervoltab-1-k(a4,d1.w),d0
  83.         mulu.w  chanpervol+2-k(a4,d6.w),d0
  84.         move.w  d0,-(sp)
  85.         move.b  (sp)+,ioa_Volume+1(a1)
  86.         moveq   #ADCMD_PERVOL,d0
  87.         bsr     doquickreq
  88. \next
  89.         dbra    d2,\loop
  90. \out
  91.         movem.l (sp)+,d2/d6-d7/a4/a6
  92.         rts
  93.  
  94. *** NoteSysAudioPri - Set the audio channel allocation priority
  95.         xdef    NoteSysAudioPri,_NoteSysAudioPri,@NoteSysAudioPri
  96. * d0 = New audio allocation priority (-128 to 127)
  97. _NoteSysAudioPri
  98.         move.l  4(sp),d0
  99. NoteSysAudioPri
  100. @NoteSysAudioPri
  101.         move.b  d0,allocpri
  102.         rts
  103.  
  104. *** noteinfo - Return information about this note player
  105. noteinfo
  106.         lea     \noteinfo(pc),a0
  107.         move.l  a0,d0
  108.         rts
  109.  
  110. \noteinfo
  111.         dc.b    VERSION
  112.         dc.b    REVISION
  113.         dc.b    3 ; NOTEF_CHIPSAMPLES!NOTEF_PERIOD
  114.         dc.b    4
  115.         dc.l    \noteplayername
  116. \noteinfoend
  117.  
  118. \noteplayername         dc.b    "Amiga audio.device NotePlayer",0
  119.         even
  120.  
  121. *** noteinit - Initialize the note player
  122. * d0.b = Number of channels needed (1-128)
  123. * a0 = Preferred stereo orientation for each channel (NULL = Amiga default)
  124. * Returns: d0 = Zero if successful, pointer to error message on failure
  125. noteinit
  126.         movem.l d2/a2/a4/a6,-(sp)
  127.         move.l  4,a6
  128.         lea     k,a4
  129.  
  130.         cmp.b   #4,d0                           ; Number of channels requested
  131.         bhi     \toomanychannels
  132.         move.b  d0,channels-k(a4)
  133.  
  134.         lea     chanpos-k(a4),a1                ; Copy channel position preference array
  135.         clr.l   (a1)
  136.         move.l  a0,d1
  137.         bz      \defchanprefs
  138. \chanprefsloop
  139.         move.b  (a0)+,(a1)+
  140.         subq.b  #1,d0
  141.         bhi     \chanprefsloop
  142. \defchanprefs
  143.  
  144.         bsr     notefinish                      ; Stop anything we were doing before
  145.  
  146.         bset.b  #0,volinitflag-k(a4)            ; Make sure master volume is initialized
  147.         bnz     \masvolinit
  148.         moveq   #$40,d0
  149.         move.b  d0,mastervoltab-1+1-k(a4)
  150.         move.b  d0,mastervoltab-1+2-k(a4)
  151.         move.b  d0,mastervoltab-1+4-k(a4)
  152.         move.b  d0,mastervoltab-1+8-k(a4)
  153. \masvolinit
  154.  
  155.         lea     sitport-k+MP_MSGLIST(a4),a1     ; Initialize sitport
  156.         NEWLIST a1
  157.         move.b  #NT_MSGPORT,sitport-k+LN_TYPE(a4)
  158.         move.b  #PA_IGNORE,sitport-k+MP_FLAGS(a4)
  159.  
  160.         lea     taskport-k+MP_MSGLIST(a4),a1    ; Initialize taskport
  161.         NEWLIST a1
  162.         move.b  #NT_MSGPORT,taskport-k+LN_TYPE(a4)
  163.         move.b  #PA_IGNORE,taskport-k+MP_FLAGS(a4)      ; The task will change this right away
  164.  
  165.         lea     sitport-k(a4),a0                ; Initialize the first IO request
  166.         move.l  a0,ioreqs+MN_REPLYPORT-k(a4)
  167.         clr.l   ioreqs+ioa_Length-k(a4)         ; Don't allocate channels on opening the device
  168.  
  169.         lea     \audiodevname(pc),a0            ; Open audio.device
  170.         moveq   #0,d0
  171.         lea     ioreqs-k(a4),a1
  172.         moveq   #0,d1
  173.         jsr     _LVOOpenDevice(a6)
  174.         tst.b   d0
  175.         bne     \openfail
  176.  
  177.         move.w  #(IOREQS-1)*ioa_SIZEOF/4-1,d0   ; Replicate the IO request
  178.         lea     ioreqs-k(a4),a0
  179.         lea     ioa_SIZEOF(a0),a1
  180.         move.b  #NT_REPLYMSG,LN_TYPE(a0)
  181. \rep
  182.         move.l  (a0)+,(a1)+
  183.         dbra    d0,\rep
  184.  
  185.         moveq   #IOREQS-1,d2                    ; Collect all requests on the sitport
  186.         lea     ioreqs-k(a4),a2
  187. \collect
  188.         move.l  a2,a1
  189.         jsr     _LVOReplyMsg(a6)
  190.         lea     ioa_SIZEOF(a2),a2
  191.         dbra    d2,\collect
  192.  
  193.         move.w  #(NT_TASK<<8)!(100),tc-k+LN_TYPE(a4)    ; Initialize task
  194.         move.l  #\notetaskname,tc-k+LN_NAME(a4)
  195.         move.l  #stack,tc-k+TC_SPLOWER(a4)
  196.         lea     stack+STACKSIZE-k(a4),a0
  197.         move.l  a0,tc-k+TC_SPUPPER(a4)
  198.         move.l  a0,tc-k+TC_SPREG(a4)
  199.         lea     tc-k+TC_MEMENTRY(a4),a0
  200.         NEWLIST a0
  201.  
  202.         lea     tc-k(a4),a1                     ; Start the task
  203.         lea     notetask(pc),a2
  204.         jsr     _LVOAddTask(a6)
  205.         tst.l   d0
  206.         bnz     \taskok
  207.         cmp.w   #36,LIB_VERSION(a6)
  208.         bhs     \addtaskfail
  209. \taskok
  210.         st.b    taskadded-k(a4)
  211.  
  212.         move.b  channels-k(a4),d2               ; Try to allocate the channels we need now
  213.         subq.b  #1,d2
  214.         clr.l   chanallocflags-k(a4)
  215. \allocloop
  216.         bsr     notestop
  217.         subq.b  #1,d2
  218.         bhs     \allocloop
  219.  
  220.         moveq   #0,d0
  221. \out
  222.         movem.l (sp)+,d2/a2/a4/a6
  223.         rts
  224.  
  225. \openfail
  226.         clr.l   ioreqs+IO_DEVICE-k(a4)
  227.         pea     \openfailmes(pc)
  228.         bra     \errerr
  229. \addtaskfail
  230.         pea     \addtaskfailmes(pc)
  231.         bra     \errerr
  232. \toomanychannels
  233.         pea     \toomanychannelsmes(pc)
  234. \errerr
  235.         bsr     notefinish
  236.         move.l  (sp)+,d0
  237.         bra     \out
  238.  
  239. \audiodevname           dc.b    "audio.device",0
  240.  
  241. \notetaskname           dc.b    "NotePlayer",0
  242.  
  243. \toomanychannelsmes     dc.b    "Too many channels requested",0
  244. \addtaskfailmes         dc.b    "Unable to create NotePlayer task",0
  245. \openfailmes            dc.b    "Unable to open audio.device",0
  246.  
  247.         even
  248.  
  249. *** notefinish - Shut down the note player, free audio hardware
  250. notefinish
  251.         movem.l d2/a2/a4/a6,-(sp)
  252.         lea     k,a4
  253.         move.l  4,a6
  254.  
  255.         jsr     _LVOForbid(a6)                  ; Maybe not necessary, but just in case...
  256.  
  257.         sub.l   a1,a1                           ; Point both ports to this task for closing down
  258.         jsr     _LVOFindTask(a6)
  259.         move.l  d0,sitport+MP_SIGTASK-k(a4)
  260.         move.w  #(PA_SIGNAL<<8)!(SIGB_SINGLE),sitport+MP_FLAGS-k(a4)
  261.         move.l  d0,taskport+MP_SIGTASK-k(a4)
  262.         move.w  #(PA_SIGNAL<<8)!(SIGB_SINGLE),taskport+MP_FLAGS-k(a4)
  263.  
  264.         tst.b   taskadded-k(a4)                 ; Get rid of the task
  265.         bz      \notask
  266.         lea     tc-k(a4),a1
  267.         jsr     _LVORemTask(a6)
  268.         clr.b   taskadded-k(a4)
  269. \notask
  270.  
  271.         jsr     _LVOPermit(a6)
  272.  
  273.         tst.l   ioreqs+IO_DEVICE-k(a4)
  274.         bz      \audioclosed
  275.  
  276.         moveq   #4-1,d2                         ; Free all audio channels
  277. \freechans
  278.         lea     ioreqs-k(a4),a1
  279.         move.w  #ADCMD_FREE,IO_COMMAND(a1)
  280.         move.b  chanmasks-k(a4,d2),IO_UNIT+3(a1)
  281.         jsr     _LVODoIO(a6)
  282.         dbra    d2,\freechans
  283. \alreadyfree
  284.         clr.l   chanmasks-k(a4)
  285.  
  286.         moveq   #IOREQS-1,d2                    ; Abort any IO requests still in progress
  287.         lea     ioreqs-k(a4),a2
  288. \abortreqs
  289.         move.l  a2,a1
  290.         jsr     _LVOAbortIO(a6)
  291.         move.l  a2,a1
  292.         jsr     _LVOWaitIO(a6)
  293.         lea     ioa_SIZEOF(a2),a2
  294.         dbra    d2,\abortreqs
  295.  
  296.         lea     ioreqs-k(a4),a1                 ; Close the audio.device
  297.         jsr     _LVOCloseDevice(a6)
  298.         clr.l   ioreqs+IO_DEVICE-k(a4)
  299. \audioclosed
  300.  
  301.         movem.l (sp)+,d2/a2/a4/a6
  302.         rts
  303.  
  304. *** findquickreq - Find an available IOAudio and prepare it for quick I/O
  305. * d2.b = Channel number (NOT.B to set ADIOF_SYNCCYCLE)
  306. * a4 = k
  307. * Returns:
  308. * NZ if successful, a1 = Ready IOAudio (still on sitport)
  309. * Z if failed, d0 = 0
  310. * d2.w = Real channel number
  311. * d6.w = Channel number * 4
  312. * d7.b = Flags for IORequest
  313. findquickreq
  314.         move.l  sitport+MP_MSGLIST+LH_HEAD,a1   ; Find any available request
  315.         move.l  LN_SUCC(a1),d0                  ; (Don't even need to Disable around this)
  316.         bz      \out
  317.  
  318.         moveq   #IOF_QUICK,d7                   ; Find channel and flags
  319.         ext.w   d2
  320.         bpl     \nosync
  321.         moveq   #IOF_QUICK!ADIOF_SYNCCYCLE,d7
  322.         not.w   d2
  323. \nosync
  324.  
  325.         move.b  chanmasks-k(a4,d2.w),IO_UNIT+3(a1)
  326.         bz      \nochan
  327.  
  328.         move.w  d2,d6
  329.         lsl.w   #2,d6
  330.  
  331.         move.b  d7,IO_FLAGS(a1)
  332. \out
  333.         rts
  334.  
  335. \nochan
  336.         bsr     allocbounce
  337.         moveq   #0,d0
  338.         rts
  339.  
  340. *** findreq - Find an available IOAudio and prepare it for slow I/O
  341. * d2.w = Real channel number
  342. * d7.b = Flags for IORequest
  343. * a4 = k
  344. * Returns:
  345. * NZ if successful, a1 = Ready IOAudio (removed from sitport), a6 = IO_DEVICE from IO request
  346. * Z if failed, d0 = 0
  347. findreq
  348.         move.l  4,a6                            ; Remove an IO request
  349.         lea     sitport,a0
  350.         jsr     _LVOGetMsg(a6)
  351.         tst.l   d0
  352.         bz      \out
  353.         move.l  d0,a1
  354.  
  355.         move.b  chanmasks-k(a4,d2.w),IO_UNIT+3(a1)
  356.  
  357.         move.l  IO_DEVICE(a1),a6                ; Find audio.device address
  358.  
  359.         moveq   #1,d0
  360. \out
  361.         rts
  362.  
  363. *** notestop - Stop any currently playing note in a channel
  364. * May be called from interrupt code level 4 or lower
  365. * d2.b = Channel number (NOT.B to stop only after the current cycle completes)
  366. notestop
  367.         moveq   #0,d0
  368.         moveq   #0,d1
  369.         ; fall through...
  370. *** notestart - Start playing a note immediately
  371. * May be called from interrupt code level 4 or lower
  372. * d2.b = Channel number (NOT.B to make the change only after the current cycle completes)
  373. * a0 = One-shot sample data
  374. * d0.l = One-shot sample length (0 if no one-shot part)
  375. * a1 = Repeat sample data
  376. * d1.l = Repeat sample length (0 if no repeat part)
  377. * d3.w = Sample frequency (if 0, then high word contains period)
  378. * d4.w = Volume at which to play sample (0-$100)
  379. notestart
  380.         movem.l d0-d2/d6-d7/a0-a2/a4/a6,-(sp)
  381.         lea     k,a4
  382.  
  383.         tst.b   d2                              ; Stop current note
  384.         bpl     \stopnow
  385. \stopcycle                                      ; Stop at the end of this cycle
  386.         bsr     findquickreq
  387.         bz      \out
  388.         move.l  chanoneshot-k(a4,d6.w),d0       ; Cancel the repeat part if we haven't gotten to it yet
  389.         bz      \canrepout
  390.         move.l  d0,a0
  391.         cmp.b   #NT_MESSAGE,LN_TYPE(a0)
  392.         bne     \canrepout
  393.         move.l  chanrepeat-k(a4,d6.w),d0
  394.         bz      \canrepout
  395.         move.b  IO_UNIT+3(a1),d1
  396.         move.l  a1,-(sp)
  397.         move.l  d0,a1
  398.         cmp.b   IO_UNIT+3(a1),d1
  399.         bne     \canrepout2
  400.         move.l  IO_DEVICE(a1),a6
  401.         jsr     DEV_ABORTIO(a6)
  402. \canrepout2
  403.         move.l  (sp)+,a1
  404. \canrepout
  405.         moveq   #ADCMD_FINISH,d0
  406.         bsr     doquickreq
  407.         bz      \stopout
  408.         bra     \out
  409. \stopnow                                        ; Stop immediately
  410.         bsr     findquickreq
  411.         bz      \out
  412.         moveq   #CMD_FLUSH,d0
  413.         bsr     doquickreq
  414.         bnz     \out
  415. \stopout
  416.  
  417.         sub.l   a2,a2                           ; One-shot part
  418.         tst.l   (sp)
  419.         bz      \nooneshot
  420.         bsr     findreq
  421.         bz      \out
  422.         move.w  #CMD_WRITE,IO_COMMAND(a1)
  423.         move.l  20(sp),ioa_Data(a1)
  424.         move.l  (sp),ioa_Length(a1)
  425.         move.w  #1,ioa_Cycles(a1)
  426.         move.b  #ADIOF_PERVOL,IO_FLAGS(a1)
  427.         move.l  a1,a2
  428.         bsr     calcper
  429.         bsr     calcvol
  430.         jsr     DEV_BEGINIO(a6)
  431. \nooneshot
  432.         move.l  a2,chanoneshot-k(a4,d6.w)
  433.  
  434.         sub.l   a2,a2                           ; Repeat part
  435.         tst.l   4(sp)
  436.         bz      \norepeat
  437.         bsr     findreq
  438.         bz      \out
  439.         move.w  #CMD_WRITE,IO_COMMAND(a1)
  440.         move.l  24(sp),ioa_Data(a1)
  441.         move.l  4(sp),ioa_Length(a1)
  442.         moveq   #0,d7
  443.         move.w  d7,ioa_Cycles(a1)
  444.         tst.l   (sp)
  445.         bnz     \pervolalreadyset
  446.         moveq   #ADIOF_PERVOL,d7
  447.         bsr     calcper
  448.         bsr     calcvol
  449.         move.w  d0,ioa_Volume(a1)
  450. \pervolalreadyset
  451.         move.b  d7,IO_FLAGS(a1)
  452.         move.l  a1,a2
  453.         jsr     DEV_BEGINIO(a6)
  454. \norepeat
  455.         move.l  a2,chanrepeat-k(a4,d6.w)
  456.  
  457. \out
  458.         movem.l (sp)+,d0-d2/d6-d7/a0-a2/a4/a6
  459.         rts
  460.  
  461. *** calcper - Calculate period and insert it in an IOAudio
  462. * d3.l = Frequency/period value
  463. * d6.w = Channel * 4
  464. * a1 = IOAudio to put period into
  465. * a4 = k
  466. calcper
  467.         tst.w   d3
  468.         bnz     \freq
  469.         swap    d3
  470.         move.w  d3,ioa_Period(a1)
  471.         move.w  d3,chanpervol-k(a4,d6.w)
  472.         swap    d3
  473.         rts
  474. \freq
  475.         move.l  #CLOCK,d0
  476.         divu.w  d3,d0
  477.         move.w  d0,ioa_Period(a1)
  478.         move.w  d0,chanpervol-k(a4,d6.w)
  479.         rts
  480.  
  481. *** calcvol - Calculate volume and insert it in an IOAudio
  482. * d4.w = Volume (0-$100)
  483. * d6.w = Channel * 4
  484. * a1 = IOAudio to put period into
  485. * a4 = k
  486. calcvol
  487.         move.w  d4,chanpervol+2-k(a4,d6.w)
  488.         move.w  IO_UNIT+2(a1),d1
  489.         moveq   #0,d0
  490.         move.b  mastervoltab-1-k(a4,d1.w),d0
  491.         mulu.w  d4,d0
  492.         move.w  d0,-(sp)
  493.         move.b  (sp)+,ioa_Volume+1(a1)
  494.         rts
  495.  
  496. *** notefreqvol - Change the frequency and volume of the currently playing note
  497. * May be called from interrupt code level 4 or lower
  498. * d2.b = Channel number (NOT.B to make the change only after the current cycle completes)
  499. * d3.w = New sample frequency (if 0, then high word contains period)
  500. * d4.w = New volume (0-$100)
  501. notefreqvol
  502.         movem.l d2/d6-d7/a4/a6,-(sp)
  503.         lea     k,a4
  504.  
  505.         bsr     findquickreq
  506.         bz      \out
  507.         bsr     calcper
  508.         bsr     calcvol
  509.         moveq   #ADCMD_PERVOL,d0
  510.         bsr     doquickreq
  511. \out
  512.         movem.l (sp)+,d2/d6-d7/a4/a6
  513.         rts
  514.  
  515. *** notefreq - Change the frequency of the currently playing note
  516. * May be called from interrupt code level 4 or lower
  517. * d2.b = Channel number (NOT.B to make the change only after the current cycle completes)
  518. * d3.w = New sample frequency (if 0, then high word contains period)
  519. notefreq
  520.         movem.l d2/d6-d7/a4/a6,-(sp)
  521.         lea     k,a4
  522.  
  523.         bsr     findquickreq
  524.         bz      \out
  525.  
  526.         bsr     calcper
  527.  
  528.         move.w  IO_UNIT+2(a1),d1
  529.         moveq   #0,d0
  530.         move.b  mastervoltab-1-k(a4,d1.w),d0
  531.         mulu.w  chanpervol+2-k(a4,d6.w),d0
  532.         move.w  d0,-(sp)
  533.         move.b  (sp)+,ioa_Volume+1(a1)
  534.  
  535.         moveq   #ADCMD_PERVOL,d0
  536.         bsr     doquickreq
  537. \out
  538.         movem.l (sp)+,d2/d6-d7/a4/a6
  539.         rts
  540.  
  541. *** notevol - Change the volume of the currently playing note
  542. * May be called from interrupt code level 4 or lower
  543. * d2.b = Channel number (NOT.B to make the change only after the current cycle completes)
  544. * d4.w = New volume (0-$100)
  545. notevol
  546.         movem.l d2/d6-d7/a4/a6,-(sp)
  547.         lea     k,a4
  548.  
  549.         bsr     findquickreq
  550.         bz      \out
  551.  
  552.         cmp.w   chanpervol+2-k(a4,d6.w),d4
  553.         beq     \out
  554.  
  555.         move.w  chanpervol-k(a4,d6.w),ioa_Period(a1)
  556.  
  557.         bsr     calcvol
  558.  
  559.         moveq   #ADCMD_PERVOL,d0
  560.         bsr     doquickreq
  561. \out
  562.         movem.l (sp)+,d2/d6-d7/a4/a6
  563.         rts
  564.  
  565. *** doquickreq - Send an immediate command to the audio.device
  566. * d0.w = IO Command
  567. * a1 = IOAudio
  568. * d2.w = Real channel number
  569. * a4 = k
  570. * Returns: Z = Successful, NZ = Audio error (generally channel stolen)
  571. doquickreq
  572.         move.w  d0,IO_COMMAND(a1)
  573.         move.l  IO_DEVICE(a1),a6
  574.         move.l  a1,-(sp)
  575.         jsr     DEV_BEGINIO(a6)
  576.         move.l  (sp)+,a1
  577.         tst.b   IO_ERROR(a1)
  578.         bnz     \err
  579.         rts
  580.  
  581. \err
  582.         bsr     allocbounce
  583.         moveq   #1,d0
  584.         rts
  585.  
  586. *** allocbounce - Bounce an IOAudio to the note player task for allocation
  587. * a1 = IOAudio (on sitport)
  588. * d2.w = Real channel number
  589. * a4 = k
  590. * Returns:
  591. * a6 = SysBase
  592. allocbounce
  593.         move.l  4,a6
  594.  
  595.         bset.b  #0,chanallocflags-k(a4,d2.w)    ; Prevent multiple allocation requests from piling up
  596.         bnz     \out
  597.  
  598.         move.l  a1,-(sp)                        ; Take the request off the sitport
  599.         jsr     _LVODisable(a6)
  600.         move.l  (sp),a1
  601.         jsr     _LVORemove(a6)
  602.         jsr     _LVOEnable(a6)
  603.         move.l  (sp)+,a1
  604.  
  605.         move.w  #ADCMD_ALLOCATE,IO_COMMAND(a1)  ; Setup request for channel allocation
  606.         move.b  allocpri-k(a4),LN_PRI(a1)
  607.         move.w  d2,ioa_WriteMsg+LN_NAME(a1)     ; Hide the channel number in the message
  608.  
  609.         tst.b   chanpos-k(a4,d2.w)              ; Find the appropriate channel preference array
  610.         bz      \middle
  611.         bpl     \right
  612. \left
  613.         lea     \leftarray(pc),a0
  614.         bra     \gotarray
  615. \right
  616.         lea     \rightarray(pc),a0
  617.         bra     \gotarray
  618. \middle
  619.         lea     \middlearray(pc),a0
  620. \gotarray
  621.         move.l  a0,ioa_Data(a1)
  622.         moveq   #4,d0
  623.         move.l  d0,ioa_Length(a1)
  624.  
  625.         move.l  #taskport,MN_REPLYPORT(a1)      ; Send it off to our task
  626.         jsr     _LVOReplyMsg(a6)
  627.  
  628. \out
  629.         rts
  630.  
  631. \leftarray      dc.b    %1000,%0001,%0100,%0010
  632. \rightarray     dc.b    %0100,%0010,%1000,%0001
  633. \middlearray    dc.b    %1000,%0100,%0010,%0001
  634.  
  635. *** notetask - Entrypoint for NotePlayer task
  636. notetask
  637.         move.l  4,a6
  638.  
  639.         lea     taskport,a2                     ; Activate the taskport
  640.         move.l  #tc,MP_SIGTASK(a2)
  641.         move.w  #(PA_SIGNAL<<8)!(SIGB_SINGLE),MP_FLAGS(a2)
  642.  
  643. \loop
  644.         move.l  a2,a0                           ; Grab waiting messages
  645.         jsr     _LVOGetMsg(a6)
  646.         tst.l   d0
  647.         bz      \nomsgs
  648.         move.l  d0,a3
  649.  
  650.         move.w  ioa_WriteMsg+LN_NAME(a3),d2     ; Channel number (d2)
  651.  
  652.         move.l  a3,a1                           ; IORequest is all ready for action
  653.         jsr     _LVODoIO(a6)
  654.  
  655.         tst.b   IO_ERROR(a3)                    ; Remember the allocated channel
  656.         bnz     \allocfailed
  657.         move.b  IO_UNIT+3(a3),chanmasks-taskport(a2,d2.w)
  658. \allocfailed
  659.  
  660.         move.l  #sitport,MN_REPLYPORT(a3)       ; Back to the menagerie it goes
  661.         move.l  a3,a1
  662.         jsr     _LVOReplyMsg(a6)
  663.  
  664.         clr.b   chanallocflags-taskport(a2,d2.w)
  665.  
  666.         bra     \loop
  667.  
  668. \nomsgs
  669.         move.l  a2,a0
  670.         jsr     _LVOWaitPort(a6)
  671.         bra     \loop
  672.  
  673.         bss     __MERGED
  674.  
  675. k:
  676.  
  677. chanmasks       ds.b    4                       ; IO_UNIT for each channel
  678. chanpos         ds.b    4                       ; Stereo position of each channel
  679. chanallocflags  ds.b    4                       ; Set if an allocation request is pending
  680. chanpervol      ds.w    4*2                     ; Current period (+0) and volume (+2) (0-$100)
  681. chanoneshot     ds.l    4                       ; IOAudio of current one-shot sample
  682. chanrepeat      ds.l    4                       ; IOAudio of current repeat sample
  683.  
  684. mastervoltab    ds.b    8                       ; Master volume (0-$40)
  685.  
  686. sitport         ds.b    MP_SIZE                 ; "Silent" port where idle IORequests sit around
  687. taskport        ds.b    MP_SIZE                 ; Port where allocation requests go to
  688.  
  689. ioreqs          ds.b    ioa_SIZEOF*IOREQS       ; A bunch of IO requests for playing notes and such
  690.  
  691. tc              ds.b    TC_SIZE                 ; Task control block
  692. stack           ds.b    STACKSIZE               ; Stack for task
  693.  
  694. taskadded       ds.b    1                       ; The allocation task is currently running
  695.  
  696. channels        ds.b    1                       ; Number of channels we're using
  697.  
  698. volinitflag     ds.b    1                       ; Nonzero if master volume's been initialized
  699.  
  700. allocpri        ds.b    1                       ; Audio channel allocation priority
  701.  
  702.         end
  703.